/**
 * @file open_protocol_tiny.c
 * @author Zhenling Pan(ryan.pan@dji.com)
 * @brief
 * @version 0.1
 * @date 2022-01-29
 *
 * @copyright Copyright (c) 2022
 *
 */

#include "open_protocol_tiny.h"
#include "crc.h"
#include "mem_mang.h"
#include "stddef.h"
#include "string.h"

#define GET_OFFSET(type, member)                                               \
  ((size_t)(&(((typeof(type) *)0)->member))) //从结构体中获得成员的位置偏移

#define OPEN_PROTOCOL_TAIL_CRC_SIZE (sizeof(uint16_t))
#define OPEN_PROTOCOL_NON_DATA_SIZE                                            \
  (sizeof(open_protocol_header_t) + OPEN_PROTOCOL_TAIL_CRC_SIZE)
#define OPEN_PROTOCOL_DATA_MAX_BYTE                                            \
  (OPEN_PROTOCOL_PACK_MAX_BYTE - OPEN_PROTOCOL_NON_DATA_SIZE)

uint32_t open_procotol_heap_use = 0;
uint16_t open_all_error_cnt[6] = {0};
uint32_t open_all_success_cnt = 0;
uint32_t open_pack_seq_cnt = 0;

OPEN_PROTOCOL_MUTEX_DECLARE(open_protocol_mutex);

/**
 * @brief 初始化
 *
 */
void open_protocol_init(void) { OPEN_PROTOCOL_MUTEX_INIT(open_protocol_mutex); }

/**
 * @brief 协议解包器初始化
 *
 * @param p 协议解包器对象结构体
 */
void open_protocol_sniffer_init(open_protocol_sniffer_t *p) {
  memset(p, 0, sizeof(open_protocol_sniffer_t));
  INIT_LIST_HEAD(&(p->recv_pack_list));
}
/**
 * @brief 协议探测器
 *
 * @param buf   输入数据缓冲区的地址
 * @param len   输入数据的长度
 * @param p     协议解包器对象结构体
 * @param time  当前时间（毫秒），没有计时器可填0
 */
void open_protocol_sniffer(uint8_t *buf, uint16_t len,
                           open_protocol_sniffer_t *p, uint32_t time) {
  if ((p->step != 0) && (time - p->time > OPEN_PROTOCOL_UNPACK_TIMEOUT) &&
      (time > p->time)) {
    if (p->step > 3) {
      open_protocol_free_pack_node(p->recv_node);
    }
    p->step = 0;
    p->err_num++;
    open_all_error_cnt[5]++;
    p->time = time;
  }

  if (len == 0) {
    return;
  }

  p->time = time;

  uint16_t crc16_read;
  uint16_t read_ptr = 0;

  while (read_ptr < len) {
    switch (p->step) {
    /* 寻找SOF */
    case 0:
      if (buf[read_ptr] == OPEN_PROTOCOL_SOF) {
        p->step = 1;
        p->pre_header.data[0] = OPEN_PROTOCOL_SOF;
      } else {
        p->err_num++;
        open_all_error_cnt[0]++;
      }
      read_ptr++;
      break;

    /* 接收协议包长度、版本 */
    case 1:
    case 2:
      p->pre_header.data[p->step] = buf[read_ptr];
      p->step++;
      read_ptr++;
      break;

    /* 校验SOF、包长度和版本，校验通过分配内存接收剩余字节 */
    case 3:
      p->pre_header.data[p->step] = buf[read_ptr];

      if ((crc8_checksum_get(p->pre_header.data, 3, OPEN_PROTOCOL_CRC8_INIT) ==
           p->pre_header.u.crc8) &&
          (p->pre_header.u.data_len + sizeof(open_protocol_header_t) + 2 <
           OPEN_PROTOCOL_MAX_PACK_LENGTH)) {
        uint16_t need_malloc =
            p->pre_header.u.data_len + sizeof(open_protocol_pack_node_t) + 2;
        if (need_malloc + open_procotol_heap_use <
            OPEN_PROTOCOL_HEAP_MALLOC_MAX) {
          OPEN_PROTOCOL_MUTEX_LOCK(open_protocol_mutex);
          /* 校验通过 */
          p->recv_node =
              (open_protocol_pack_node_t *)(OPEN_PROTOCOL_HEAP_MALLOC(
                  need_malloc));
          if (p->recv_node != 0) {
            /* 内存分配成功 */
            p->step = 4;
            p->data_cnt = 4;
            p->recv_node->len =
                p->pre_header.u.data_len + sizeof(open_protocol_header_t) + 2;
            uint8_t *dst = (uint8_t *)(&(p->recv_node->header));
            memcpy(dst, p->pre_header.data, 4);
            read_ptr++;
            open_procotol_heap_use += need_malloc;

          } else {
            /* 内存分配失败 */
            p->step = 0;
            read_ptr += p->pre_header.u.data_len +
                        sizeof(open_protocol_header_t) + 2 - 4;
            p->err_num++;
            open_all_error_cnt[1]++;
          }
          OPEN_PROTOCOL_MUTEX_UNLOCK(open_protocol_mutex);
        } else {
          /* 协议分配内存超出限制，不允许在分配*/
          read_ptr +=
              p->pre_header.u.data_len + sizeof(open_protocol_header_t) + 2 - 4;
          p->step = 0;
          p->err_num++;
          open_all_error_cnt[2]++;
        }
      } else {
        /* 校验失败 */
        p->step = 0;
        p->err_num++;
        open_all_error_cnt[3]++;
      }
      break;

    /* 接收数据 */
    case 4:
      if (p->data_cnt <
          p->pre_header.u.data_len + sizeof(open_protocol_header_t) + 2) {
        uint16_t copy_num = p->pre_header.u.data_len +
                            sizeof(open_protocol_header_t) + 2 - p->data_cnt;
        if (copy_num > len - read_ptr) {
          copy_num = len - read_ptr;
        }
        uint8_t *dst = (uint8_t *)(&(p->recv_node->header));
        memcpy(dst + p->data_cnt, buf + read_ptr, copy_num);
        p->data_cnt += copy_num;
        read_ptr += copy_num;
        if (p->data_cnt >=
            p->pre_header.u.data_len + sizeof(open_protocol_header_t) + 2) {
          p->step = 5;
        } else {
          break;
        }
      } else {
        p->step = 5;
      }

    /* 接收完成，校验处理 */
    case 5:
      crc16_read = p->recv_node->header.data[p->pre_header.u.data_len];
      crc16_read |= p->recv_node->header.data[p->pre_header.u.data_len + 1]
                    << 8;

      if (crc16_checksum_get((uint8_t *)(&(p->recv_node->header)),
                             p->pre_header.u.data_len +
                                 sizeof(open_protocol_header_t),
                             OPEN_PROTOCOL_CRC16_INIT) == crc16_read) {
        /* 校验正确，添加到接收列表中，等待轮询处理 */
        OPEN_PROTOCOL_MUTEX_LOCK(open_protocol_mutex);
        p->success_num++;
        open_all_success_cnt++;
        list_add_tail(&(p->recv_node->node), &(p->recv_pack_list));
        OPEN_PROTOCOL_MUTEX_UNLOCK(open_protocol_mutex);
      } else {
        open_protocol_free_pack_node(p->recv_node);
        p->err_num++;
        open_all_error_cnt[4]++;
      }
      p->step = 0;
      break;
    }
  }
}

/**
 * @brief 获取一个接收到的包数据节点，如果全部接收完成，则返回空
 *
 * @param p  协议解包器对象结构体
 * @return open_protocol_pack_node_t*   包数据节点结构体
 */
open_protocol_pack_node_t *
open_protocol_get_pack_node(open_protocol_sniffer_t *p) {
  open_protocol_pack_node_t *node = NULL;
  OPEN_PROTOCOL_MUTEX_LOCK(open_protocol_mutex);
  if (!list_empty(&(p->recv_pack_list))) {
    node = (open_protocol_pack_node_t *)(p->recv_pack_list.next);
    list_del(&(node->node));
  }
  OPEN_PROTOCOL_MUTEX_UNLOCK(open_protocol_mutex);
  return node;
}

/**
 * @brief
 * 释放一个包数据节点，处理完包数据节点后需调用此函数释放内存，否则会导致内存泄漏
 *
 * @param node  包数据节点结构体地址
 */
void open_protocol_free_pack_node(open_protocol_pack_node_t *node) {
  if (node != NULL) {
    OPEN_PROTOCOL_MUTEX_LOCK(open_protocol_mutex);
    open_procotol_heap_use -=
        node->len + offsetof(open_protocol_pack_node_t, header);
    OPEN_PROTOCOL_HEAP_FREE(node);
    OPEN_PROTOCOL_MUTEX_UNLOCK(open_protocol_mutex);
  }
}

/**
 * @brief 正常包组包，Buff长度需要大于组包后数据的全部长度
 *
 * @param buff      组包数据存放BUFF地址
 * @param buff_len  BUFF长度
 * @param cmd_id    命令码
 * @param src_addr  源地址
 * @param dst_addr  目的地址
 * @param need_ack  是否需要ACK
 * @param data      包数据段BUFF地址
 * @param data_len  包数据段长度
 * @return int      大于0:组包数据长度 -1:缓冲区BUFF长度不足
 */
int open_normal_pack(uint8_t *buff, uint32_t buff_len, uint16_t cmd_id,
                     uint16_t src_addr, uint16_t dst_addr, uint8_t need_ack,
                     uint8_t *data, uint32_t data_len) {
  if (buff_len < OPEN_PROTOCOL_NON_DATA_SIZE + data_len) {
    return -1;
  }

  open_protocol_header_t *header = (open_protocol_header_t *)(buff);
  uint16_t *tail_crc = (uint16_t *)(header->data + data_len);

  header->sof = OPEN_PROTOCOL_SOF;
  header->data_len = data_len;
  header->attr = 0;
  header->seq_num = open_pack_seq_cnt++;
  header->src_addr = src_addr;
  header->dst_addr = dst_addr;
  header->is_ack = 0;
  header->need_ack = need_ack ? 1 : 0;
  header->crc8 = crc8_checksum_get((uint8_t *)header,
                                   GET_OFFSET(open_protocol_header_t, crc8),
                                   OPEN_PROTOCOL_CRC8_INIT);
  header->cmd_id = cmd_id;

  if (data_len > 0) {
    memcpy(header->data, data, data_len);
  }

  *tail_crc = crc16_checksum_get(
      (uint8_t *)(header), sizeof(open_protocol_header_t) + header->data_len,
      OPEN_PROTOCOL_CRC16_INIT);

  return OPEN_PROTOCOL_NON_DATA_SIZE + data_len;
}

/**
 * @brief ACK包组包，Buff长度需要大于组包后数据的全部长度
 *
 * @param buff          组包数据存放BUFF地址
 * @param buff_len      BUFF长度
 * @param req_header    请求包结构体的地址
 * @param data          包数据段BUFF地址
 * @param data_len      包数据段长度
 * @return int          大于0:组包数据长度 -1:缓冲区BUFF长度不足
 */
int open_ack_pack(uint8_t *buff, uint32_t buff_len,
                  open_protocol_header_t *req_header, uint32_t local_addr,
                  uint8_t *data, uint32_t data_len) {
  if (buff_len < OPEN_PROTOCOL_NON_DATA_SIZE + data_len) {
    return -1;
  }

  open_protocol_header_t *header = (open_protocol_header_t *)(buff);
  uint16_t *tail_crc = (uint16_t *)(header->data + data_len);

  header->sof = OPEN_PROTOCOL_SOF;
  header->data_len = data_len;
  header->attr = 0;
  header->crc8 = crc8_checksum_get((uint8_t *)header,
                                   GET_OFFSET(open_protocol_header_t, crc8),
                                   OPEN_PROTOCOL_CRC8_INIT);
  header->seq_num = req_header->seq_num;
  header->src_addr = local_addr;
  header->dst_addr = req_header->src_addr;
  header->is_ack = 1;
  header->need_ack = 0;
  header->cmd_id = req_header->cmd_id;

  if (data_len > 0) {
    memcpy(header->data, data, data_len);
  }

  *tail_crc = crc16_checksum_get(
      (uint8_t *)(header), sizeof(open_protocol_header_t) + header->data_len,
      OPEN_PROTOCOL_CRC16_INIT);

  return OPEN_PROTOCOL_NON_DATA_SIZE + data_len;
}

/**
 * @brief 标记一个包节点已被转发
 *
 * @param node  包数据节点结构体地址
 */
void open_protocol_forward_pack_node(open_protocol_pack_node_t *node) {
  if (node != NULL) {
    OPEN_PROTOCOL_MUTEX_LOCK(open_protocol_mutex);
    open_procotol_heap_use -=
        node->len + offsetof(open_protocol_pack_node_t, header);
    OPEN_PROTOCOL_MUTEX_UNLOCK(open_protocol_mutex);
  }
}
